//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<initors.cpp>
///	@path	~/src/engine/xcore/
///	@date	2008/03/25
///	@desc	.

#include "config/config.h"

#include "lib/system/thread_debug.h"

#include "engine/xcore/initors.h"

namespace xeres {

	// Initors
	Initors::Initors( void )
	{
		BuildDefault();
	}
	// ~Initors
	Initors::~Initors( void )
	{

	}

	namespace
	{
		// startup filter
		class _FirstFrameFilter
		{
		public:
			//@ ctor
			_FirstFrameFilter(
				std::vector< function<void(void)> >& initCallback ,
				std::vector< function<void(void)> >& finalCallback )
				: m_perThread( initCallback ) , m_final( finalCallback )
			{
			}

			//@ ctor[copy]
			_FirstFrameFilter( const _FirstFrameFilter& rhs )
				: m_perThread( rhs.m_perThread ) , m_final( rhs.m_final )
			{
			}

			// nodes
			DepStagesFilterAction operator() ( const WString& path , const WString& name , function<void(void)> m_callback )
			{
				TRACK_FUNCTION_SCOPE_MSG( _S("Running first frame stage '%s/%s'.") ,
					path.c_str() , name.c_str() );
				TRACE_INFO( _S("Running first frame stage '%s/%s'.") , path.c_str() , name.c_str() );

				// filter out perthread call
				if( path == _S("thread/per-thread/") )
				{
					m_perThread.push_back( m_callback );
				}
				else if( path == _S("final/") )
				{
					m_final.push_back( m_callback );
				}
				else
				{
					m_callback();
				}
				return DEP_STAGES_ACTION_NEXT_NODE;
			}

		private:

		//@ data

			// per-thread call
			std::vector< function<void(void)> >& m_perThread;

			// final call
			std::vector< function<void(void)> >& m_final;
		};

		class _FirstFrameProgress
		{
		public:

			// progress print
			void operator () ( float progress )
			{
				// cue
				TRACE_INFO( _S("Startup progress ...... %2.1f") , progress * 100.f );
			}
		};

		// startup filter to filter out finalize call
		class _FirstFrameFiniFilter
		{
		public:
			//@ ctor
			_FirstFrameFiniFilter( std::vector< function<void(void)> >& finiCallback )
				: m_perThread( finiCallback )
			{
			}

			//@ ctor[copy]
			_FirstFrameFiniFilter( const _FirstFrameFiniFilter& rhs )
				: m_perThread( rhs.m_perThread )
			{
			}

			// nodes
			DepStagesFilterAction operator () ( const WString& path , const WString& name , const function<void(void)>& m_callback )
			{
				// filter out perthread call
				if( path == _S("thread/per-thread/") )
				{
					m_perThread.push_back( m_callback );
				}
				return DEP_STAGES_ACTION_NEXT_NODE;
			}

		private:

		//@ data

			// per-thread call
			std::vector< function<void(void)> >& m_perThread;
		};

		// cleanup filter
		class _LastFrameFilter
		{
		public:

			// nodes
			DepStagesFilterAction operator () ( const WString& path , const WString& name , const function<void(void)>& m_callback )
			{
				TRACK_FUNCTION_SCOPE_MSG( _S("Running last frame stage '%s/%s'.") ,
					path.c_str() , name.c_str() );
				TRACE_INFO( _S("Running last frame stage '%s/%s'.") , path.c_str() , name.c_str() );

				// filter out perthread call
				if( path == _S("thread/per-thread/") )
				{
					// skip
				}
				else
				{
					m_callback();
				}
				return DEP_STAGES_ACTION_NEXT_NODE;
			}
		};

		typedef dep_stages< function<void(void)> >::null_filter NullFilter;
	}

	// FirstFrame
	void Initors::FirstFrame( void )
	{
		TRACE_INFO( _S("Initor: First frame...") );

		// invoke initializers
		std::vector< function<void(void)> > finals;
		if( ! m_initStages.traversal(
			_FirstFrameFilter( m_threadInitCallback , finals ) ,
			NullFilter() ,
			_FirstFrameProgress() ) )
		{
			FATAL_ABORT( _S("Failed to execute initialize stages.") );
		}

		// final stage calls
		if( ! m_finiStages.traversal(
			_FirstFrameFiniFilter( m_threadFiniCallback ) ,
			NullFilter() , NullFilter() ) )
		{
			FATAL_ABORT( _S("Failed to execute finalize stages.") );
		}

		for( size_t i = 0 , total = finals.size() ; i < total ; ++i )
		{
			finals[i]();
		}
	}
	// LastFrame
	void Initors::LastFrame( void )
	{
		// execute stages
		if( ! m_finiStages.traversal( _LastFrameFilter() , NullFilter() , NullFilter() ) )
		{
			FATAL_ABORT( _S("Failed to execute finalize stages.") );
		}

		// reset all.
		BuildDefault();
	}
	// QueueInitializer
	bool Initors::QueueInitializer( const WString& stage , const WString& dep , function<void(void)> init_call )
	{
		RUNNING_ON_THREAD( _S("main") );
		return m_initStages.insert_node( stage , dep , init_call );
	}
	// QueueFinalizer
	bool Initors::QueueFinalizer( const WString& stage , const WString& dep , function<void(void)> fini_call )
	{
		RUNNING_ON_THREAD( _S("main") );
		return m_finiStages.insert_node( stage , dep , fini_call );
	}
	// BuildDefault
	void Initors::BuildDefault( void )
	{
		m_initStages.clear();
		m_finiStages.clear();
		m_threadInitCallback.clear();
		m_threadFiniCallback.clear();

		// initialize stages
		m_initStages.insert_stage(	_S(".")		,	_S("")			);
		m_initStages.insert_stage(	_S("thread")	,	_S(".")		);
		m_initStages.insert_stage(	_S("device")	,	_S("thread")	);
		m_initStages.insert_stage(	_S("renderer")	,	_S("device")	);
		m_initStages.insert_stage(	_S("resource")	,	_S("renderer")	);
		m_initStages.insert_stage(	_S("final")	,	_S("resource")	);

		// finalize stages
		m_finiStages.insert_stage(	_S("~")		,	_S("thread")	);
		m_finiStages.insert_stage(	_S("thread")	,	_S("device")	);
		m_finiStages.insert_stage(	_S("device")	,	_S("renderer")	);
		m_finiStages.insert_stage(	_S("renderer")	,	_S("resource")	);
		m_finiStages.insert_stage(	_S("resource")	,	_S("")			);

		// special stages
		// thread, start all threads , then define per-thread
		m_initStages.insert_stage(	_S("./register")		 ,	_S("")			  );
		m_initStages.insert_stage(	_S("thread/.")			 ,	_S("")			  );
		m_initStages.insert_stage(	_S("thread/per-thread") ,	_S(".")		  );
		m_initStages.insert_stage(	_S("thread/start")		 ,	_S("per-thread") );

		m_finiStages.insert_stage(	_S("thread/per-thread") ,	_S("")	);
	}

} // namespace xeres
